L’objectif de ce TP est d’illustrer les notions abordées pour la méthode DBSCAN et pour la clasisifcation hiérarchique. Les librairies R nécessaires pour ce TP :
## Pour faire le TP
library(mclust)
library(cluster)
library(clusterSim)
library(factoextra)
library(FactoMineR)
library(reticulate)
library(ggplot2)
library(reshape2)
library(circlize)
library(viridis)
Les librairies Python nécessaires pour ce TP :
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import plotly.express as px
from sklearn.cluster import DBSCAN
from sklearn.metrics.cluster import adjusted_rand_score
from sklearn.metrics import silhouette_samples, silhouette_score
from sklearn.neighbors import NearestNeighbors
from scipy.cluster.hierarchy import dendrogram, linkage
from sklearn.cluster import AgglomerativeClustering
from yellowbrick.cluster import KElbowVisualizer
from sklearn.cluster import KMeans
On reprend dans ce second TP les données wine
disponibles sur la page moodle du cours. On charge ici les données.
wine<-read.table("wine.txt",header=T)
wine$Qualite = as.factor(wine$Qualite)
wine$Type = factor(wine$Type, labels = c("blanc", "rouge"))
wineinit<-wine
wine[,-c(1,2)]<-scale(wine[,-c(1,2)],center=T,scale=T)
head(wine)
## Qualite Type AcidVol AcidCitr SO2lbr SO2tot Densite
## 1352 medium rouge 1.638714588 -1.92626362 -1.2083376 -1.15967786 -0.46497450
## 5493 medium blanc -0.068544417 -1.35617574 -0.7004747 -0.85707581 -0.33499781
## 5153 medium blanc -0.800226847 -0.59605856 0.5409681 -0.02047014 1.32391517
## 5308 medium blanc -0.007570881 0.92417581 1.7824108 1.27893867 1.08790487
## 3866 medium blanc 0.419243870 0.03737243 -0.5311870 0.99413674 0.03783006
## 694 medium rouge 0.785085086 0.03737243 -0.4747578 0.19313131 1.27260858
## Alcool
## 1352 1.14546909
## 5493 -1.12092616
## 5153 -1.29526426
## 5308 -1.29526426
## 3866 0.09944051
## 694 -0.94658806
On fait une ACP pour la visualisation des résultats dans la suite
resacp<-PCA(wine,quali.sup=c(1,2), scale.unit = TRUE,graph=FALSE)
Et on prépare aussi pour Python
winepy=r.wine
winequant=winepy
winequant=winequant.drop(columns=['Type','Qualite'])
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler();
scaler.fit(winequant);
winequant=scaler.transform(winequant)
from sklearn.decomposition import PCA
mypca = PCA(n_components=3);
mypca.fit(winequant);
dataPCA=mypca.fit_transform(winequant)
Question : Dans un premier temps, utilisez
l’algorithme DBSCAN avec les paramètres minPts= 7 et
eps= 1 à l’aide de la fonction dbscan() de la
librairie dbscan. Quels sont les effectifs par classe ?
Combien d’individus ne sont pas classés ?
# A COMPLETER
minPts<-7
eps<-1
res.db <- dbscan::dbscan(...)
table(...)
fviz_cluster(res.db, wine[,-c(1:2)], geom="point",ellipse="FALSE")+
theme(legend.position="none")+
xlab("")+ylab("")+ggtitle("Avec DBSCAN")
Correction :
minPts<-7
eps<-1
res.db <- dbscan::dbscan(wine[,-c(1,2)],eps=eps,minPts=minPts)
table(res.db$cluster)
##
## 0 1 2 3 4
## 138 360 75 22 5
fviz_cluster(res.db, wine[,-c(1:2)], geom="point",ellipse="FALSE")+
theme(legend.position="none")+
xlab("")+ylab("")+ggtitle("Avec DBSCAN")
Question : Pour étudier l’influence des paramètres
minPts et eps, évaluez le nombre de classes
obtenues et le nombre d’individus non classés pour différentes valeurs
de ces paramètres.
minPts <- ...
eps <- ...
NBCluster <- matrix(0,nrow=length(minPts),ncol=length(eps))
NBNonCl <-matrix(0,nrow=length(minPts),ncol=length(eps))
for (i in 1:length(minPts)){
for (j in 1:length(eps)){
res<-dbscan::dbscan(wine[,-c(1,2)], eps=eps[j], minPts=minPts[i])
NBCluster[i,j] <- ...
NBNonCl[i,j] <- ...
}
}
df<-data.frame(eps=rep(eps,each=length(minPts)),
minPts=as.factor(rep(minPts,length(eps))),
NBCluster=c(NBCluster),
NBNonCl=c(NBNonCl)*100/nrow(wine))
ggplot(df,aes(x=eps,y=NBCluster,col=minPts))+geom_point()+geom_line()
ggplot(df,aes(x=eps,y=NBNonCl,col=minPts))+geom_point()+geom_line()
Correction :
minPts <-seq(5,15,1)
eps <-seq(0.5,2,0.1)
NBCluster <- matrix(0,nrow=length(minPts),ncol=length(eps))
NBNonCl <-matrix(0,nrow=length(minPts),ncol=length(eps))
for (i in 1:length(minPts)){
for (j in 1:length(eps)){
res<-dbscan::dbscan(wine[,-c(1,2)], eps=eps[j], minPts=minPts[i])
NBCluster[i,j] <- length(table(res$cluster))-1
NBNonCl[i,j] <- sum(res$cluster==0)
}
}
df<-data.frame(eps=rep(eps,each=length(minPts)),
minPts=as.factor(rep(minPts,length(eps))),
NBCluster=c(NBCluster),
NBNonCl=c(NBNonCl)*100/nrow(wine))
ggplot(df,aes(x=eps,y=NBCluster,col=minPts))+geom_point()+geom_line()
ggplot(df,aes(x=eps,y=NBNonCl,col=minPts))+geom_point()+geom_line()
Question : Pour une valeur de minPts=7,
tracez le graphe de distance kNN afin de choisir le paramètre
eps. Vous pouvez utiliser la fonction
kNNdistplot(). Qu’en pensez-vous ?
# A COMPLETER
Correction :
dbscan::kNNdistplot(wine[,-c(1,2)], k = 6)
abline(h=1.2)
res.db <- dbscan::dbscan(wine[,-c(1,2)],eps=1.2,minPts=7)
table(res.db$cluster)
##
## 0 1
## 72 528
Question : Dans un premier temps, utilisez
l’algorithme DBSCAN avec les paramètres minPts= 7 et eps= 1 à l’aide de
la fonction DBSCAN de la librairie
scikit-learn. Quels sont les effectifs par classe ? Combien
d’individus ne sont pas classés ?
# A COMPLETER
from sklearn.cluster import DBSCAN
dbsc = DBSCAN(....).fit(....)
pd.crosstab(....,"freq")
import plotly.express as px
import pandas as pd
pca_df = pd.DataFrame({
"Dim1" : dataPCA[:,0],
"Dim2" : dataPCA[:,1],
"DBSCAN" : pd.Categorical(dbsc.labels_)
})
fig=px.scatter(pca_df,x="Dim1",y="Dim2",color="DBSCAN")
fig.show()
Correction :
from sklearn.cluster import DBSCAN
import seaborn as sns
dbsc = DBSCAN(eps=1, min_samples=7).fit(winequant)
pd.crosstab(dbsc.labels_,"freq")
## col_0 freq
## row_0
## -1 138
## 0 360
## 1 75
## 2 22
## 3 5
import pandas as pd
pca_df = pd.DataFrame({
"Dim1" : dataPCA[:,0],
"Dim2" : dataPCA[:,1],
"DBSCAN" : pd.Categorical(dbsc.labels_)
})
fig=px.scatter(pca_df,x="Dim1",y="Dim2",color="DBSCAN")
fig.show()
#sns.lmplot( x="Dim1", y="Dim2", data=pca_df, fit_reg=False, hue='DBSCAN', legend=True);
#plt.show()
Question : Pour étudier l’influence des paramètres minPts et eps, évaluez le nombre de classes obtenues et le nombre d’individus non classés pour différentes valeurs de ces paramètres.
# A COMPLETER
epsvalue= ...
minPtsvalue=...
NBCluster = np.zeros((minPtsvalue.size,epsvalue.size))
NBNonCl=np.zeros((minPtsvalue.size,epsvalue.size))
for i in np.arange(0,minPtsvalue.size,1):
for j in np.arange(0,epsvalue.size,1):
resdbsc = DBSCAN(eps=...,min_samples=...).fit(...)
NBCluster[i,j] = ...
NBNonCl[i,j] = ...
#
plt.plot(epsvalue,NBCluster[0,:], label='minPts=5', color='green')
plt.plot(epsvalue,NBCluster[4,:], label='minPts=9', color='steelblue')
plt.plot(epsvalue,NBCluster[7,:], label='minPts=12', color='purple')
plt.plot(epsvalue,NBCluster[10,:], label='minPts=15', color='red')
plt.legend()
plt.show()
plt.close()
#
plt.plot(epsvalue,NBNonCl[0,:], label='minPts=5', color='green')
plt.plot(epsvalue,NBNonCl[4,:], label='minPts=9', color='steelblue')
plt.plot(epsvalue,NBNonCl[7,:], label='minPts=12', color='purple')
plt.plot(epsvalue,NBNonCl[10,:], label='minPts=15', color='red')
plt.legend()
plt.show()
Correction :
epsvalue=np.arange(0.5, 2.1, 0.1)
minPtsvalue=np.arange(5,16,1)
NBCluster = np.zeros((minPtsvalue.size,epsvalue.size))
NBNonCl=np.zeros((minPtsvalue.size,epsvalue.size))
for i in np.arange(0,minPtsvalue.size,1):
for j in np.arange(0,epsvalue.size,1):
resdbsc = DBSCAN(eps=epsvalue[j],min_samples=minPtsvalue[i]).fit(winequant)
NBCluster[i,j] = max(resdbsc.labels_)+1
NBNonCl[i,j] = (resdbsc.labels_==-1).sum()
#
plt.plot(epsvalue,NBCluster[0,:], label='minPts=5', color='green')
plt.plot(epsvalue,NBCluster[4,:], label='minPts=9', color='steelblue')
plt.plot(epsvalue,NBCluster[7,:], label='minPts=12', color='purple')
plt.plot(epsvalue,NBCluster[10,:], label='minPts=15', color='red')
plt.legend()
plt.show()
plt.close()
#
plt.plot(epsvalue,NBNonCl[0,:], label='minPts=5', color='green')
plt.plot(epsvalue,NBNonCl[4,:], label='minPts=9', color='steelblue')
plt.plot(epsvalue,NBNonCl[7,:], label='minPts=12', color='purple')
plt.plot(epsvalue,NBNonCl[10,:], label='minPts=15', color='red')
plt.legend()
plt.show()
Question : Pour une valeur de minPts=7,
tracez le graphe de distance kNN afin de choisir le paramètre
eps. Qu’en pensez-vous ?
# A COMPLETER
import numpy as np
from sklearn.neighbors import NearestNeighbors
neighbors = NearestNeighbors(.....)
neighbors_fit = neighbors.fit(.....)
distances, indices = neighbors_fit.kneighbors(......)
distancesmean = np.sort(distances.sum(axis=1)/6,axis=0)
plt.plot(distancesmean)
plt.axhline(y=....., linewidth=1, linestyle='dashed', color='k')
plt.ylabel("k-NN distance")
plt.xlabel("Points sorted by distanc)")
plt.show()
dbscopt = DBSCAN(eps=...., min_samples=....).fit(winequant)
pd.crosstab(dbscopt.labels_,"freq")
Correction :
import numpy as np
from sklearn.neighbors import NearestNeighbors
neighbors = NearestNeighbors(n_neighbors=6)
neighbors_fit = neighbors.fit(winequant)
distances, indices = neighbors_fit.kneighbors(winequant)
distancesmean = np.sort(distances.sum(axis=1)/6,axis=0)
plt.plot(distancesmean)
plt.axhline(y= 1 , linewidth=1, linestyle='dashed', color='k')
plt.ylabel("k-NN distance")
plt.xlabel("Points sorted by distance")
plt.show()
dbscopt = DBSCAN(eps=1, min_samples=7).fit(winequant)
pd.crosstab(dbscopt.labels_,"freq")
## col_0 freq
## row_0
## -1 138
## 0 360
## 1 75
## 2 22
## 3 5
Dans cette section, nous nous intéressons à la classification hiérarchique que nous allons étudier avec R et python également.
Question : A l’aide de la fonction
hclust, faites une classification hiérarchique des données
de vins avec les mesures d’agrégation single,
complete et average respectivement. Comparez
visuellement les dendrogrammes associés. Commentez.
# A COMPLETER
hclustsingle<-hclust(...)
hclustcomplete<-hclust(...)
hclustaverage<-hclust(...)
# Dendrogramme
plot(hclustsingle,hang=-1,labels=FALSE)
...
fviz_dend(hclustsingle,show_labels=FALSE)
...
Correction :
Ajustement des trois classifications hiérarchiques à l’aide de la
fonction hclust() :
d<-dist(wine[,-c(1,2)],method="euclidean")
hclustsingle<-hclust(d,method="single")
hclustcomplete<-hclust(d,method="complete")
hclustaverage<-hclust(d,method="average")
Visualisation des trois dendrogrammes :
#plot(hclustsingle,hang=-1,labels=FALSE)
#plot(hclustcomplete,hang=-1,labels=FALSE)
#plot(hclustaverage,hang=-1,labels=FALSE)
fviz_dend(hclustsingle,show_labels=FALSE)
## Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as
## of ggplot2 3.3.4.
## ℹ The deprecated feature was likely used in the factoextra package.
## Please report the issue at <]8;;https://github.com/kassambara/factoextra/issueshttps://github.com/kassambara/factoextra/issues]8;;>.
fviz_dend(hclustcomplete,show_labels=FALSE)
fviz_dend(hclustaverage,show_labels=FALSE)
Question : Déduisez du dendrogramme avec la mesure
d’agrégation complete une classification en 3 classes. Vous
pouvez utiliser la fonction cutree(). Comparez-la avec les
variables Qualité et Type. Commentez.
# A COMPLETER
Correction :
ClassK3<-cutree(hclustcomplete,3)
table(ClassK3)
## ClassK3
## 1 2 3
## 99 273 228
# Comparaison avec Qualité
table(ClassK3,wine$Qualite)
##
## ClassK3 bad good medium
## 1 6 13 80
## 2 9 39 225
## 3 4 58 166
adjustedRandIndex(ClassK3,wine$Qualite)
## [1] 0.009529003
# Comparaison avec Type
table(ClassK3,wine$Type)
##
## ClassK3 blanc rouge
## 1 20 79
## 2 178 95
## 3 227 1
adjustedRandIndex(ClassK3,wine$Type)
## [1] 0.2000887
Question : Tracez la distribution des variables
quantitatives de wine en fonction de la classification en 3
classes de la question précédente. Commentez.
df<-data.frame(wine[,-c(1,2)],Class=as.factor(ClassK3))
df<-melt(df,id="Class")
ggplot(df,aes(x=variable,y=value))+geom_violin(aes(fill=Class))
Correction :
df<-data.frame(wine[,-c(1,2)],Class=as.factor(ClassK3))
df<-melt(df,id="Class")
ggplot(df,aes(x=variable,y=value))+geom_violin(aes(fill=Class))
Question : Dans cette question et pour les suivantes, on se focalise sur la mesure d’agrégation de Ward. Ajustez une classification hiérarchique avec la mesure de Ward. Que représentent les hauteurs du dendrogramme dans ce cas ?
# A COMPLETER
hward<-hclust(...)
fviz_dend(hward,show_labels=FALSE)
Correction :
hward<-hclust(d,method="ward.D2")
fviz_dend(hward,show_labels=FALSE)
Question : Déterminez le nombre de classes à
retenir avec l’indice de Calinski_Harabasz. Vous pouvez vous aider de la
fonction ìndex.G1() de la librairie
clusterSim. Tracez la classification obtenue sur le
dendrogramme et sur le premier plan factorielde l’ACP.
# A completer
ClustCH<-cutree(hward,....)
fviz_dend(hward,show_labels=FALSE,k=which.max(CH)+1)
fviz_pca_ind(resacp,geom = c("point"),habillage=as.factor(ClustCH))
Correction :
CH<-NULL
Kmax<-20
for (k in 2:Kmax){
CH<-c(CH,index.G1(wine[,-c(1,2)],cutree(hward,k)))
}
daux<-data.frame(NbClust=2:Kmax,CH=CH)
ggplot(daux,aes(x=NbClust,y=CH))+geom_line()+geom_point()
ClustCH<-cutree(hward,which.max(CH)+1)
fviz_dend(hward,show_labels=FALSE,k=which.max(CH)+1)
fviz_pca_ind(resacp,geom = c("point"),habillage=as.factor(ClustCH))
table(ClustCH,wine$Qualite)
##
## ClustCH bad good medium
## 1 5 7 122
## 2 5 36 193
## 3 6 55 91
## 4 3 12 65
table(ClustCH,wine$Type)
##
## ClustCH blanc rouge
## 1 20 114
## 2 233 1
## 3 138 14
## 4 34 46
Question : Déterminez le nombre de classes à
retenir avec le critère Silhouette. Vous pouvez vous aider de la
fonction ìndex.S() de la librairie clusterSim.
Comparez avec la classification de la question précédente.
# A COMPLETER
Correction :
Silhou<-NULL
Kmax<-20
for (k in 2:Kmax){
Silhou<-c(Silhou,index.S(d,cutree(hward,k)))
}
daux<-data.frame(NbClust=2:Kmax,Silhouette=Silhou)
ggplot(daux,aes(x=NbClust,y=Silhouette))+geom_line()+geom_point()
Question : Comparez la classification obtenue avec la méthode des Kmeans dans le TP 1 et celle obtenue à la question précédente.
# A COMPLETER
Correction :
reskmeans<-kmeans(wine[,-c(1,2)],4)
table(reskmeans$cluster,cutree(hward,4))
##
## 1 2 3 4
## 1 18 9 10 63
## 2 110 0 3 1
## 3 4 151 0 13
## 4 2 74 139 3
adjustedRandIndex(reskmeans$cluster,cutree(hward,4))
## [1] 0.4995215
library(circlize)
clust1F<-paste("ClKm-",reskmeans$cluster,sep="")
clust2F<-paste("ClCAH-",cutree(hward,4),sep="")
library(viridis)
mycolor <- viridis(8, alpha = 1, begin = 0, end = 1, option = "H")
mycolor <- mycolor[sample(1:8)]
chordDiagram(table(clust1F,clust2F),grid.col=mycolor)
Question : A l’aide de la fonction
linkage, faites une classification hiérarchique des données
de vins avec les mesures d’agrégation single,
complete et average respectivement. Comparez
visuellement les dendrogrammes associés. Commentez.
# A COMPLETER
from scipy.cluster.hierarchy import dendrogram, linkage
hsingle=linkage(...)
hcomplete=linkage(...)
haverage=linkage(...)
dendrogram(....,no_labels=True,color_threshold=0);
plt.show()
Correction :
Ajustement des trois classifications hiérarchiques à l’aide de la
fonction hclust() :
from scipy.cluster.hierarchy import dendrogram, linkage
hsingle=linkage(winequant,method='single')
hcomplete=linkage(winequant,method='complete')
haverage=linkage(winequant,method='average')
Visualisation des trois dendrogrammes :
dendrogram(hsingle,no_labels=True,color_threshold=0);
plt.show()
dendrogram(hcomplete,no_labels=True,color_threshold=0);
plt.show()
dendrogram(haverage,no_labels=True,color_threshold=0);
plt.show()
Question : Déterminez une classification en 3
classes avec la mesure d’agrégation complete. Comparez-la
avec les variables Qualité et Type. Commentez.
# A COMPLETER
from sklearn.cluster import AgglomerativeClustering
hierarchical_cluster = AgglomerativeClustering(.....)
ClustK3 = hierarchical_cluster.fit_predict(winequant)
# Comparer avec Qualite et Type
.....
Correction :
from sklearn.cluster import AgglomerativeClustering
hierarchical_cluster = AgglomerativeClustering(n_clusters=3, affinity='euclidean', linkage='complete')
ClustK3 = hierarchical_cluster.fit_predict(winequant)
pd.crosstab(ClustK3,"freq")
# Comparaison avec Qualité
## col_0 freq
## row_0
## 0 228
## 1 99
## 2 273
pd.crosstab(ClustK3,winepy["Qualite"])
## Qualite bad good medium
## row_0
## 0 4 58 166
## 1 6 13 80
## 2 9 39 225
adjusted_rand_score(ClustK3,winepy["Qualite"])
# Comparaison avec Type
## 0.009529003116147837
pd.crosstab(ClustK3,winepy["Type"])
## Type blanc rouge
## row_0
## 0 227 1
## 1 20 79
## 2 178 95
adjusted_rand_score(ClustK3,winepy["Type"])
## 0.20008874452412492
Question : Tracez la distribution des variables
quantitatives de wine en fonction de la classification en 3
classes de la question précédente. Commentez.
import plotly.express as px
aux=winepy.assign(Clust=ClustK3)
del aux["Qualite"]
del aux["Type"]
sm = aux.melt(id_vars='Clust')
fig=px.box(sm,x="Clust",y="value",facet_col="variable",facet_col_wrap=3,color="Clust")
fig.show()
Correction :
import plotly.express as px
aux=winepy.assign(Clust=ClustK3)
del aux["Qualite"]
del aux["Type"]
sm = aux.melt(id_vars='Clust')
fig=px.box(sm,x="Clust",y="value",facet_col="variable",facet_col_wrap=3,color="Clust")
fig.show()
Question : Dans cette question et pour les suivantes, on se focalise sur la mesure d’agrégation de Ward. Ajustez une classification hiérarchique avec la mesure de Ward. Que représentent les hauteurs du dendrogramme dans ce cas ?
# A COMPLETER
Correction :
hward=linkage(winequant,method='ward')
dendrogram(hward,no_labels=True,color_threshold=0);
plt.show()
Question : Déterminez le nombre de classes à
retenir avec l’indice de Calinski-Harabasz. Vous pouvez vous aider de la
fonction KElbowVisualizer de la librairie
yellowbrick. Tracez la classification obtenue sur le
premier plan factoriel de l’ACP.
from yellowbrick.cluster import KElbowVisualizer
model = AgglomerativeClustering()
visualizer = KElbowVisualizer(......);
visualizer.fit(winequant)
visualizer.show()
Correction :
from yellowbrick.cluster import KElbowVisualizer
model = AgglomerativeClustering()
visualizer = KElbowVisualizer(model, k=(2,20), metric="calinski_harabasz",timings=False);
# Fit data to visualizer
visualizer.fit(winequant)
# Finalize and render figure
## KElbowVisualizer(ax=<AxesSubplot:>,
## estimator=AgglomerativeClustering(n_clusters=19), k=(2, 20),
## metric='calinski_harabasz', timings=False)
visualizer.show()
#visualizer.close()
modelfin = AgglomerativeClustering(n_clusters=4, affinity='euclidean', linkage='ward')
modelfin.fit(winequant)
## AgglomerativeClustering(n_clusters=4)
pd.crosstab(modelfin.labels_,"freq")
## col_0 freq
## row_0
## 0 234
## 1 134
## 2 152
## 3 80
pca_df = pd.DataFrame({
"Dim1" : dataPCA[:,0],
"Dim2" : dataPCA[:,1],
"CAH" : pd.Categorical(modelfin.labels_)
})
fig=px.scatter(pca_df,x="Dim1", y="Dim2",color="CAH")
fig.show()
Question : Déterminez le nombre de classes à
retenir avec le critère Silhouette. Vous pouvez vous aider de la
fonction KElbowVisualizer de la librairie
yellowbrick. Comparez avec la classification de la question
précédente.
# A COMPLETER
Correction :
visualizer = KElbowVisualizer(model, k=(2,20), metric="silhouette",timings=False);
visualizer.fit(winequant);
visualizer.show()
Question : Comparez la classification obtenue avec la méthode des Kmeans dans le TP 1 et celle obtenue à la question précédente.
# A COMPLETER
Correction :
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters = 4, init = 'k-means++', max_iter = 300, n_init = 10, random_state = 0)
kmeans.fit(winequant)
## KMeans(n_clusters=4, random_state=0)
modelfin2 = AgglomerativeClustering(n_clusters=4, affinity='euclidean', linkage='ward')
modelfin2.fit(winequant)
## AgglomerativeClustering(n_clusters=4)
pd.crosstab(modelfin2.labels_,kmeans.labels_)
## col_0 0 1 2 3
## row_0
## 0 153 74 7 0
## 1 4 2 18 110
## 2 0 139 10 3
## 3 13 3 63 1
adjusted_rand_score(modelfin2.labels_,kmeans.labels_)
## 0.5059827100331064